//
//  FetchModel.m
//  Basic
//
//  Created by 裴烨烽 on 16/5/21.
//  Copyright © 2016年 BasicPod. All rights reserved.
//

#import "FetchModel.h"
#import <objc/runtime.h>

#import "UIAlertView+Customise.h"               // category
#import "SBJSON.h"
#pragma mark - NSArray + FetchModel
@interface NSArray (FetchModel)
- (NSArray *)modelArrayWithClass:(Class)modelClass;

@end

@implementation NSArray (FetchModel)

- (NSArray *)modelArrayWithClass:(Class)modelClass{
    NSMutableArray *modelArray = [NSMutableArray array];
    for (id object in self) {
        if ([object isKindOfClass:[NSArray class]]) {
            [modelArray addObject:[object modelArrayWithClass:modelClass]];
        } else if ([object isKindOfClass:[NSDictionary class]]){
            [modelArray addObject:[[modelClass alloc] initWithJSONDict:object]];
        } else {
            [modelArray addObject:object];
        }
    }
    return modelArray;
}
@end

#pragma mark - NSDictionary + FetchModel
@interface NSDictionary (FetchModel)
- (NSDictionary *)modelDictionaryWithClass:(Class)modelClass;

@end

@implementation NSDictionary (FetchModel)

- (NSDictionary *)modelDictionaryWithClass:(Class)modelClass{
    NSMutableDictionary *modelDictionary = [NSMutableDictionary dictionary];
    for (NSString *key in self) {
        id object = [self objectForKey:key];
        if ([object isKindOfClass:[NSDictionary class]]) {
            [modelDictionary setObject:[[modelClass alloc] initWithJSONDict:object] forKey:key];
        }else if ([object isKindOfClass:[NSArray class]]){
            [modelDictionary setObject:[object modelArrayWithClass:modelClass] forKey:key];
        }else{
            [modelDictionary setObject:object forKey:key];
        }
    }
    return modelDictionary;
}

@end

#pragma mark - FetchModel

static const char *FecthModelKeyMapperKey;
static const char *FetchModelPropertiesKey;

@interface FetchModel(){
    NSURLSessionDataTask *requestOperation;
}
@property (nonatomic,strong)AFHTTPSessionManager *operationManager;
- (void)setupCachedKeyMapper;
- (void)setupCachedProperties;

@end


@implementation FetchModel

// 1.

-(void)dealloc{
    if (requestOperation){
        [requestOperation cancel];
    }
}

-(void)setUP{
    if (!self.timeOutTime){
        self.timeOutTime = 10;
    }
}

-(void)setIsReturnLog:(BOOL)isReturnLog{
    if (isReturnLog){
        _isReturnLog = YES;
    } else {
        _isReturnLog = NO;
    }
}

-(instancetype)init{
    self = [super init];
    if (self){
        [self setUP];
        [self setupCachedKeyMapper];
        [self setupCachedProperties];
    }
    return self;
}

- (instancetype)initWithJSONDict:(NSDictionary *)dict{
    self = [self init];
    if (self) {
        [self injectJSONData:dict];
    }
    return self;
}

#pragma mark - 
-(AFHTTPSessionManager *)operationManager{
    if (!_operationManager){
        NSString *baseURLString = @"";
        if (API_VER.length){
            if (API_PORT.length){
                baseURLString = [NSString stringWithFormat:@"https://%@:%@/%@/",API_HOST,API_PORT,API_VER];
            } else {
                baseURLString = [NSString stringWithFormat:@"https://%@/%@",API_HOST,API_VER];
            }
        } else {
            if (API_PORT.length){
                baseURLString = [NSString stringWithFormat:@"https://%@:%@/%@/",API_HOST,API_PORT,API_VER];
            } else {
                baseURLString = [NSString stringWithFormat:@"https://%@/%@",API_HOST,API_VER];
            }
        }
        

        NSURL *baseURL = [NSURL URLWithString:baseURLString];
        _operationManager = [[AFHTTPSessionManager alloc]initWithBaseURL:baseURL];
        _operationManager.requestSerializer = [RequestSerializer serializer];
        _operationManager.requestSerializer.timeoutInterval = _timeOutTime;
        _operationManager.responseSerializer = [AFHTTPResponseSerializer serializer];
    }
    return _operationManager;
}




-(BOOL)isSessionValid{
    NSURL *url = self.operationManager.baseURL;
    NSArray *cookies = [[NSHTTPCookieStorage sharedHTTPCookieStorage] cookiesForURL:url];
    
    for (NSHTTPCookie *cookie in cookies) {
        if ([cookie.name isEqualToString:@"kdAuthToken"] && (cookie.expiresDate.timeIntervalSinceNow < 0)) {
            return NO;
        }
    }
    
    return YES;
}

- (void)clearCookies{
    NSURL *url = self.operationManager.baseURL;
    NSArray *cookies = [[NSHTTPCookieStorage sharedHTTPCookieStorage] cookiesForURL:url];
    
    for (NSHTTPCookie *cookie in cookies) {
        [[NSHTTPCookieStorage sharedHTTPCookieStorage] deleteCookie:cookie];
    }
}

-(void)fetchWithPath:(NSString *)path completionHandler:(FetchCompletionHandler)handler{
    NSMutableDictionary *requestParamsDic = [NSMutableDictionary dictionaryWithDictionary:_requestParams];
    
    __weak typeof(self) weakSelf = self;
    [requestOperation cancel];
    [self.operationManager POST:path parameters:requestParamsDic constructingBodyWithBlock:^(id<AFMultipartFormData>  _Nonnull formData) {
        NSDate *date = [NSDate date];
        NSDateFormatter *formatter = [[NSDateFormatter alloc]init];
        [formatter setDateFormat:@"yyyyMMddHHmmss"];
        NSString *dateStr = [formatter stringFromDate:date];
        if (_requestFileDataArr.count){
            for (int i = 0 ;i < _requestFileDataArr.count;i++){
                FetchFileModel *fileModel = [_requestFileDataArr objectAtIndex:i];
                UIImage *valueImage = fileModel.uploadImage;
                NSData *data = UIImageJPEGRepresentation(valueImage,0.7);
                NSString *keyName = fileModel.keyName;
                NSString *fileName = [NSString stringWithFormat:@"%@%d.png", dateStr,i + 1];
                
                // 1. 查找当前的数据里面的key
                NSString *name = [_requestParams objectForKey:keyName];
                if (!name.length){
                    name = [NSString stringWithFormat:@"%li",(long)arc4random()];
                }
                [formData appendPartWithFileData:data name:name fileName:fileName mimeType:@"image/png"];
            }
        }
    } progress:^(NSProgress * _Nonnull uploadProgress) {
    
    } success:^(NSURLSessionDataTask * _Nonnull task, id  _Nullable responseObject) {
        if (!weakSelf){
            return ;
        }
        // 重置相关字段为默认
        weakSelf.bizResult = NO;
        weakSelf.bizDataIsNull = NO;
        if (responseObject) {
            __strong typeof(weakSelf)strongSelf = weakSelf;
            dispatch_async(dispatch_get_main_queue(), ^{
                // 返回data
                NSData *responsData = responseObject;
                NSString *str =  [[NSString alloc]initWithData:responsData encoding:NSUTF8StringEncoding];
                SBJSON *json = [[SBJSON alloc] init];
                NSDictionary *dicWithRequestJson = [json objectWithString:str error:nil];
#ifdef DEBUG
                NSLog(@"%@",dicWithRequestJson);
#endif
                // 获取请求是否成功
                NSNumber *errorCode = [dicWithRequestJson objectForKey:@"code"];
                // 获取请求data 数据
                id data = [dicWithRequestJson objectForKey:@"data"];
                if (errorCode.integerValue == 0){              // 【成功】
                    if ([data isKindOfClass:[NSArray class]] || [data isKindOfClass:[NSDictionary class]]){
                        strongSelf.bizResult = YES;
                        [strongSelf injectJSONData:data];
                    } else if ([data isKindOfClass:[NSNull class]]){
                        _bizDataIsNull = YES;
                    } else if ([data isKindOfClass:[NSNumber class]]){
                        if ([NSStringFromClass([data class]) hasSuffix:@"CFBoolean"]){
                            _bizResult = [data boolValue];
                        }
                    }
                    handler(YES,dicWithRequestJson,nil);
                } else {                                        // 【失败】
                    NSString *errorInfo = [dicWithRequestJson objectForKey:@"msg"];
                    if (!errorInfo){
                        errorInfo = @"系统错误请联系程序员";
                    }
                    NSDictionary *dict = @{NSLocalizedDescriptionKey: errorInfo};
                    NSError *bizError = [NSError errorWithDomain:BizErrorDomain code:errorCode.integerValue userInfo:dict];
                    handler(NO,nil,bizError);
                }
            });
        }    } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
        if (!weakSelf){
            return ;
        }
        __strong typeof(self)strongSelf = weakSelf;
        
#ifdef DEBUG
        NSString *requestURL = [task.currentRequest.URL absoluteString];
        NSString *params = [[NSString alloc]initWithData:task.currentRequest.HTTPBody encoding:NSUTF8StringEncoding];
        NSLog(@"FAILURE URL:%@ \nPARAMS:%@ \nAND RESPONSE:%@", requestURL, params, task.response);
#endif
        [strongSelf showResponseCode:task.response WithBlock:^(NSInteger statusCode) {
            handler(NO,nil,error);
        }];
    }];
}

- (void)showResponseCode:(NSURLResponse *)response WithBlock:(void (^)(NSInteger statusCode))block{
    NSHTTPURLResponse* httpResponse = (NSHTTPURLResponse*)response;
    NSInteger responseStatusCode = [httpResponse statusCode];
    return block(responseStatusCode);
}

- (void)setupCachedKeyMapper{
    
    if (objc_getAssociatedObject(self.class, &FecthModelKeyMapperKey) == nil) {
        
        NSDictionary *dict = [self modelKeyJSONKeyMapper];
        if (dict.count) {
            objc_setAssociatedObject(self.class, &FecthModelKeyMapperKey, dict, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
        }
    }
}

- (void)setupCachedProperties{
    
    if (objc_getAssociatedObject(self.class, &FetchModelPropertiesKey) == nil) {
        
        NSMutableDictionary *propertyMap = [NSMutableDictionary dictionary];
        Class class = [self class];
        
        while (class != [FetchModel class]) {
            unsigned int propertyCount;
            objc_property_t *properties = class_copyPropertyList(class, &propertyCount);
            for (unsigned int i = 0; i < propertyCount; i++) {
                
                objc_property_t property = properties[i];
                const char *propertyName = property_getName(property);
                NSString *name = [NSString stringWithUTF8String:propertyName];
                const char *propertyAttrs = property_getAttributes(property);
                NSString *typeString = [NSString stringWithUTF8String:propertyAttrs];
                FetchModelProperty *modelProperty = [[FetchModelProperty alloc] initWithName:name typeString:typeString];
                if (!modelProperty.isReadonly) {
                    [propertyMap setValue:modelProperty forKey:modelProperty.name];
                }
            }
            free(properties);
            
            class = [class superclass];
        }
        objc_setAssociatedObject(self.class, &FetchModelPropertiesKey, propertyMap, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
    }
}

- (NSDictionary *)modelKeyJSONKeyMapper{
    return @{};
}

#pragma mark - FetchModel Runtime Injection

- (void)injectJSONData:(id)dataObject{
    
    NSDictionary *keyMapper = objc_getAssociatedObject(self.class, &FecthModelKeyMapperKey);
    NSDictionary *properties = objc_getAssociatedObject(self.class, &FetchModelPropertiesKey);
    
    if ([dataObject isKindOfClass:[NSArray class]]) {
        
        FetchModelProperty *arrayProperty = nil;
        Class class = NULL;
        for (FetchModelProperty *property in [properties allValues]) {
            
            NSString *valueProtocol = [property.objectProtocols firstObject];
            class = NSClassFromString(valueProtocol);
            if ([valueProtocol isKindOfClass:[NSString class]] && [class isSubclassOfClass:[FetchModel class]]) {
                arrayProperty = property;
                break;
            }
        }
        
        if (arrayProperty && class) {
            id value = [(NSArray *)dataObject modelArrayWithClass:class];
            [self setValue:value forKey:arrayProperty.name];
        }
    }else if ([dataObject isKindOfClass:[NSDictionary class]]){
        
        for (FetchModelProperty *property in [properties allValues]) {
            
            NSString *jsonKey = property.name;
            NSString *mapperKey = [keyMapper objectForKey:jsonKey];
            jsonKey = mapperKey ?: jsonKey;
            
            id jsonValue = [dataObject objectForKey:jsonKey];
            id propertyValue = [self valueForProperty:property withJSONValue:jsonValue];
            
            if (propertyValue) {
                
                [self setValue:propertyValue forKey:property.name];
            }else{
                
                id resetValue = (property.valueType == ClassPropertyTypeObject) ? nil : @(0);
                [self setValue:resetValue forKey:property.name];
            }
        }
    }
}

- (id)valueForProperty:(FetchModelProperty *)property withJSONValue:(id)value{
    
    id resultValue = value;
    if (value == nil || [value isKindOfClass:[NSNull class]]) {
        resultValue = nil;
    }else{
        if (property.valueType != ClassPropertyTypeObject) {
            
            if ([value isKindOfClass:[NSString class]]) {
                if (property.valueType == ClassPropertyTypeInt ||
                    property.valueType == ClassPropertyTypeUnsignedInt||
                    property.valueType == ClassPropertyTypeShort||
                    property.valueType == ClassPropertyTypeUnsignedShort) {
                    resultValue = [NSNumber numberWithInt:[(NSString *)value intValue]];
                }
                if (property.valueType == ClassPropertyTypeLong ||
                    property.valueType == ClassPropertyTypeUnsignedLong ||
                    property.valueType == ClassPropertyTypeLongLong ||
                    property.valueType == ClassPropertyTypeUnsignedLongLong){
                    resultValue = [NSNumber numberWithLongLong:[(NSString *)value longLongValue]];
                }
                if (property.valueType == ClassPropertyTypeFloat) {
                    resultValue = [NSNumber numberWithFloat:[(NSString *)value floatValue]];
                }
                if (property.valueType == ClassPropertyTypeDouble) {
                    resultValue = [NSNumber numberWithDouble:[(NSString *)value doubleValue]];
                }
                if (property.valueType == ClassPropertyTypeChar) {
                    //对于BOOL而言，@encode(BOOL) 为 c 也就是signed char
                    resultValue = [NSNumber numberWithBool:[(NSString *)value boolValue]];
                }
            }
        }else{
            Class valueClass = property.objectClass;
            if ([valueClass isSubclassOfClass:[FetchModel class]] &&
                [value isKindOfClass:[NSDictionary class]]) {
                resultValue = [[valueClass alloc] initWithJSONDict:value];
            }
            
            if ([valueClass isSubclassOfClass:[NSString class]] &&
                ![value isKindOfClass:[NSString class]]) {
                resultValue = [NSString stringWithFormat:@"%@",value];
            }
            
            if ([valueClass isSubclassOfClass:[NSNumber class]] &&
                [value isKindOfClass:[NSString class]]) {
                NSNumberFormatter *numberFormatter = [[NSNumberFormatter alloc] init];
                [numberFormatter setNumberStyle:NSNumberFormatterDecimalStyle];
                resultValue = [numberFormatter numberFromString:value];
            }
            
            NSString *valueProtocol = [property.objectProtocols lastObject];
            if ([valueProtocol isKindOfClass:[NSString class]]) {
                
                Class valueProtocolass = NSClassFromString(valueProtocol);
                if (valueProtocolass != nil) {
                    if ([valueProtocolass isSubclassOfClass:[FetchModel class]]) {
                        //array of models
                        if ([value isKindOfClass:[NSArray class]]) {
                            resultValue = [(NSArray *)value modelArrayWithClass:valueProtocolass];
                        }
                        //dictionary of models
                        if ([value isKindOfClass:[NSDictionary class]]) {
                            resultValue = [(NSDictionary *)value modelDictionaryWithClass:valueProtocolass];
                        }
                    }
                }
            }
        }
    }
    return resultValue;
}

-(void)fetchWithGetShareAPIPath:(NSString *)path completionHandler:(FetchCompletionHandler)handler{
    NSMutableDictionary *requestParamsDic = [NSMutableDictionary dictionaryWithDictionary:_requestParams];
    if (![[requestParamsDic allKeys] containsObject:@"key"]) {
        NSString *key = @"ecf474398748";
        requestParamsDic[@"key"] = key;
    }
    __weak typeof(self)weakSelf = self;
    [requestOperation cancel];
    [self.operationManager GET:path parameters:requestParamsDic progress:^(NSProgress * _Nonnull downloadProgress) {
        
    } success:^(NSURLSessionDataTask * _Nonnull task, id  _Nullable responseObject) {
        if (!weakSelf){
            return ;
        }
        // 重置相关字段为默认
        weakSelf.bizResult = NO;
        weakSelf.bizDataIsNull = NO;
        if (responseObject) {
            __strong typeof(weakSelf)strongSelf = weakSelf;
            dispatch_async(dispatch_get_main_queue(), ^{
                // 返回data
                NSData *responsData = responseObject;
                NSString *str =  [[NSString alloc]initWithData:responsData encoding:NSUTF8StringEncoding];
                SBJSON *json = [[SBJSON alloc] init];
                NSDictionary *dicWithRequestJson = [json objectWithString:str error:nil];
#ifdef DEBUG
                NSLog(@"%@",dicWithRequestJson);
#endif
                // 获取请求是否成功
                NSNumber *errorCode = [dicWithRequestJson objectForKey:@"retCode"];
                // 获取请求data 数据
                id data = [dicWithRequestJson objectForKey:@"result"];
                if (errorCode.integerValue == 200){              // 【成功】
                    if ([data isKindOfClass:[NSArray class]] || [data isKindOfClass:[NSDictionary class]]){
                        strongSelf.bizResult = YES;
                        [strongSelf injectJSONData:data];
                    } else if ([data isKindOfClass:[NSNull class]]){
                        _bizDataIsNull = YES;
                    } else if ([data isKindOfClass:[NSNumber class]]){
                        if ([NSStringFromClass([data class]) hasSuffix:@"CFBoolean"]){
                            _bizResult = [data boolValue];
                        }
                    }
                    handler(YES,nil,nil);
                } else {                                        // 【失败】
                    NSString *errorInfo = [dicWithRequestJson objectForKey:@"msg"];
                    if (!errorInfo){
                        errorInfo = @"系统错误请联系程序员";
                    }
                    NSDictionary *dict = @{NSLocalizedDescriptionKey: errorInfo};
                    NSError *bizError = [NSError errorWithDomain:BizErrorDomain code:errorCode.integerValue userInfo:dict];
                    handler(NO,nil,bizError);
                }
            });
        }
    } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
        if (!weakSelf) {
            return;
        }
#ifdef DEBUG
        NSString *requestURL = [task.currentRequest.URL absoluteString];
        NSString *params = [[NSString alloc]initWithData:task.currentRequest.HTTPBody encoding:NSUTF8StringEncoding];
        NSLog(@"FAILURE URL:%@ \nPARAMS:%@ \nAND RESPONSE:%@", requestURL, params, task.response);
#endif
        __strong typeof(weakSelf)strongSelf = weakSelf;
        [strongSelf showResponseCode:task.response WithBlock:^(NSInteger statusCode) {
            if (statusCode == 401){
                
            } else {
                handler(NO,nil,error);
            }
        }];
    }];
}


@end
